#ifndef __LOKI_GENERATORS_HPP
#define __LOKI_GENERATORS_HPP

#include "LokiTypeTraits.hpp"
#include "LokiTypelist.hpp"

namespace DataStructure
{
	////////////////////////////////////////////////////////////////////////////////
	// class template GenScatterHierarchy
	// Generates a scattered hierarchy starting from a typelist and a template
	// Invocation (TList is a typelist, Unit is a template of one arg):
	// GenScatterHierarchy<TList, Unit>
	// The generated class inherits all classes generated by instantiating the 
	// template 'Unit' with the types contained in TList 
	////////////////////////////////////////////////////////////////////////////////

	namespace Internal
	{
		// The following type helps to overcome subtle flaw in the original 
		// implementation of GenScatterHierarchy. 
		// The flaw is revealed when the input type list of GenScatterHierarchy 
		// contains more then one element of the same type (e.g. TYPELIST_2(int, int)). 
		// In this case GenScatterHierarchy will contain multiple bases of the same 
		// type and some of them will not be reachable (per 10.3).
		// For example before the fix the first element of Tuple<TYPELIST_2(int, int)>
		// is not reachable in any way!
		template<typename, typename> 
		struct ScatterHierarchyTag;
	}

	template<typename TList, template<typename> class Unit>
	class GenScatterHierarchy;

	template<typename T1, typename T2, template<typename> class Unit>
	class GenScatterHierarchy<Typelist<T1, T2>, Unit>
		: public GenScatterHierarchy<Internal::ScatterHierarchyTag<T1, T2>, Unit>
		, public GenScatterHierarchy<T2, Unit>
	{
	public:
		typedef Typelist<T1, T2>													TList;
		// Insure that LeftBase is unique and therefore reachable
		typedef GenScatterHierarchy<Internal::ScatterHierarchyTag<T1, T2>, Unit>	LeftBase;
		typedef GenScatterHierarchy<T2, Unit>										RightBase;

		template<typename T> 
		struct Rebind
		{
			typedef Unit<T> Result;
		};
	};

	// In the middle *unique* class that resolve possible ambiguity
	template<typename T1, typename T2, template<typename> class Unit>
	class GenScatterHierarchy<Internal::ScatterHierarchyTag<T1, T2>, Unit> 
		: public GenScatterHierarchy<T1, Unit>
	{
	};

	template<typename AtomicType, template<typename> class Unit>
	class GenScatterHierarchy 
		: public Unit<AtomicType>
	{
		typedef Unit<AtomicType>	LeftBase;

		template<typename T> 
		struct Rebind
		{
			typedef Unit<T> Result;
		};
	};

	template<template<typename> class Unit>
	class GenScatterHierarchy<NullType, Unit>
	{
		template<typename T> struct Rebind
		{
			typedef Unit<T> Result;
		};
	};

	////////////////////////////////////////////////////////////////////////////////
	// function template Field
	// Accesses a field in an object of a type generated with GenScatterHierarchy
	// Invocation (obj is an object of a type H generated with GenScatterHierarchy,
	//     T is a type in the typelist used to generate H):
	// Field<T>(obj)
	// returns a reference to Unit<T>, where Unit is the template used to generate H 
	////////////////////////////////////////////////////////////////////////////////

	template<typename T, typename H>
	typename H::template Rebind<T>::Result& Field(H& obj)
	{
		return obj;
	}

	template<typename T, typename H>
	const typename H::template Rebind<T>::Result& Field(const H& obj)
	{
		return obj;
	}

	////////////////////////////////////////////////////////////////////////////////
	// function template TupleUnit
	// The building block of tuples 
	////////////////////////////////////////////////////////////////////////////////

	template<typename T>
	struct TupleUnit
	{
		T value_;
		operator T&() { return value_; }
		operator const T&() const { return value_; }
	};

	////////////////////////////////////////////////////////////////////////////////
	// class template Tuple
	// Implements a tuple class that holds a number of values and provides field 
	//     access to them via the Field function (below) 
	////////////////////////////////////////////////////////////////////////////////

	template<typename TList>
	struct Tuple : public GenScatterHierarchy<TList, TupleUnit>
	{
	};

	////////////////////////////////////////////////////////////////////////////////
	// helper class template FieldHelper
	// See Field below
	////////////////////////////////////////////////////////////////////////////////

	template<typename H, unsigned int i> struct FieldHelper;

	template<typename H>
	struct FieldHelper<H, 0>
	{
		typedef typename H::TList::Head								ElementType;
		typedef typename H::template Rebind<ElementType>::Result	UnitType;

		enum
		{
			isTuple = Conversion<UnitType, TupleUnit<ElementType> >::sametype,
			isConst = TypeTraits<H>::isConst
		};

		typedef const typename H::LeftBase													ConstLeftBase;
		typedef typename Select<isConst, ConstLeftBase, typename H::LeftBase>::value_type	LeftBase;
		typedef typename Select<isTuple, ElementType, UnitType>::value_type					UnqualifiedResultType;
		typedef typename Select<isConst, const UnqualifiedResultType, UnqualifiedResultType>::value_type ResultType;

		static ResultType& Do(H& obj)
		{
			LeftBase& leftBase = obj;
			return leftBase;
		}
	};

	template<typename H, unsigned int i>
	struct FieldHelper
	{
		typedef typename TypeAt<typename H::TList, i>::Result		ElementType;
		typedef typename H::template Rebind<ElementType>::Result	UnitType;

		enum
		{
			isTuple = Conversion<UnitType, TupleUnit<ElementType> >::sametype,
			isConst = TypeTraits<H>::isConst
		};

		typedef const typename H::RightBase													ConstRightBase;
		typedef typename Select<isConst, ConstRightBase, typename H::RightBase>::value_type	RightBase;
		typedef typename Select<isTuple, ElementType, UnitType>::value_type					UnqualifiedResultType;
		typedef typename Select<isConst, const UnqualifiedResultType, UnqualifiedResultType>::value_type ResultType;

		static ResultType& Do(H& obj)
		{
			RightBase& rightBase = obj;
			return FieldHelper<RightBase, i - 1>::Do(rightBase);
		}
	};

	////////////////////////////////////////////////////////////////////////////////
	// function template Field
	// Accesses a field in an object of a type generated with GenScatterHierarchy
	// Invocation (obj is an object of a type H generated with GenScatterHierarchy,
	//     i is the index of a type in the typelist used to generate H):
	// Field<i>(obj)
	// returns a reference to Unit<T>, where Unit is the template used to generate H
	//     and T is the i-th type in the typelist 
	////////////////////////////////////////////////////////////////////////////////

	template<int i, typename H>
	typename FieldHelper<H, i>::ResultType& Field(H& obj)
	{
		return FieldHelper<H, i>::Do(obj);
	}

	/*template<int i, typename H>
	const typename FieldHelper<H, i>::ResultType& Field(const H& obj)
	{
		return FieldHelper<H, i>::Do(obj);
	}*/

	////////////////////////////////////////////////////////////////////////////////
	// class template GenLinearHierarchy
	// Generates a linear hierarchy starting from a typelist and a template
	// Invocation (TList is a typelist, Unit is a template of two args):
	// GenScatterHierarchy<TList, Unit>
	////////////////////////////////////////////////////////////////////////////////

	template
		<
			typename TList,
			template<typename AtomicType, typename Base> class Unit,
			typename Root = EmptyType
		>
	class GenLinearHierarchy;

	template
		<
			typename T1,
			typename T2,
			template<typename, typename> class Unit,
			typename Root
		>
	class GenLinearHierarchy<Typelist<T1, T2>, Unit, Root>
		: public Unit< T1, GenLinearHierarchy<T2, Unit, Root> >
	{
	};

	template
		<
			typename T,
			template<typename, typename> class Unit,
			typename Root
		>
	class GenLinearHierarchy<Typelist<T, NullType>, Unit, Root>
		: public Unit<T, Root>
	{
	};

	template<template<typename, typename> class Unit, typename Root>
	class GenLinearHierarchy<NullType , Unit, Root>
		: public Root // is this better: Unit<NullType, Root> ?
	{
	};
}

#endif